package com.csw.android.androidtest.module.serialize;

import android.os.Parcel;
import android.os.Parcelable;
import android.view.View;

import com.csw.android.androidtest.ui.LogViewFragment;
import com.csw.android.dev_utils.utils.LogUtils;

import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;


/**
 * Parcelable 也是一个序列化接口标记，是android嫌弃java的Serializable的性能太差，而android系统中很多跨进程
 * 通讯需要高性能，所以自己弄了个序列化实现
 * <p>
 * 总结起来就是粗暴，同时又很高效。
 */
public class ParcelableTest extends LogViewFragment {

    @Override
    public void initData() {
        super.initData();
        Observable.create(new ObservableOnSubscribe<TestData>() {
            @Override
            public void subscribe(@NonNull ObservableEmitter<TestData> emitter) throws Exception {
                TestData testData = new TestData("二", 2, 2.0, logView);
                Parcel writePacket = Parcel.obtain();
                Parcel readPacket = Parcel.obtain();
                //以下展示的是将一个Parcelable对象转化成Parcel，然后获取原始数据字节数组，可以把这些数组存入文件，
                //后续反序列化时从文件读取字节数组，再输入到Parcel中，然后反向生成对象，整个过程不涉及到任何
                //反射操作不会创建大量临时变量，更妙的是很省内存，只存储构成实例的所有基本数据类型，是android
                // 系统对序列化的一个大优化，就是实体类维护起来有点麻烦罢了。
                try {
                    //将testData装包
                    testData.writeToParcel(writePacket, 0);
                    //取得testData序列化后的字节数组
                    byte[] serializableData = writePacket.marshall();
                    LogUtils.INSTANCE.d("ParcelableTest", serializableData.length + "");
                    //将字节数组设置到Parcel中
                    readPacket.unmarshall(serializableData, 0, serializableData.length);
                    //设置好数据后需要把读取位置归零，否则位置在数据末尾读取不到任何数据。
                    readPacket.setDataPosition(0);
                    //反序列化生成新对象
                    TestData deserializableObject = TestData.CREATOR.createFromParcel(readPacket);
                    if (!emitter.isDisposed()) {
                        emitter.onNext(deserializableObject);
                    }
                    if (!emitter.isDisposed()) {
                        emitter.onComplete();
                    }
                } catch (Exception e) {
                    if (!emitter.isDisposed()) {
                        emitter.onError(e);
                    }
                } finally {
                    //android系统运作时会大量用到Parcelable，会生成很多临时的Parcel对象，所以他使用了对象
                    //池进行复用，避免频繁使用内存和回收，影响系统性能，这里要记得回收
                    writePacket.recycle();
                    readPacket.recycle();
                }
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .doOnNext(new Consumer<TestData>() {
                    @Override
                    public void accept(TestData testData) throws Exception {
                        logView.addLog(testData.v1);
                        logView.addLog(testData.v2);
                        logView.addLog(testData.v3);
                    }
                })
                .subscribe();
    }

    /**
     * Parcelable是android为了提高序列化和反序列化性能提供的一个接口，Serializable使用反射导致整个序列化过
     * 程产生很多不必要的变量，性能也有问题。Parcelable的思想是，每个类其实都是由各种类或基本数据类型组合而来，
     * 所以可以通过将实例化整为零，按照顺序把每个变量转成基本类型进行存储，反序列化时再按顺序把每个基本类型读
     * 取出来转换成实例。
     * 不过因为整个序列化和反序列化过程都是手写，所以比较繁琐，不过好在as可以自动生成Creator
     */
    public static class TestData implements Parcelable {

        private final String v1;
        private final int v2;
        private final double v3;

        private final View logView;

        public TestData(String v1, int v2, double v3, View logView) {
            this.v1 = v1;
            this.v2 = v2;
            this.v3 = v3;
            this.logView = logView;
        }

        protected TestData(Parcel in) {
            //按顺序读取
            v1 = in.readString();
            v2 = in.readInt();
            v3 = in.readDouble();
            logView = null;
        }

        public static final Creator<TestData> CREATOR = new Creator<TestData>() {
            @Override
            public TestData createFromParcel(Parcel in) {
                return new TestData(in);
            }

            @Override
            public TestData[] newArray(int size) {
                return new TestData[size];
            }
        };

        public String getV1() {
            return v1;
        }

        public int getV2() {
            return v2;
        }

        public double getV3() {
            return v3;
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            //按顺序写入
            dest.writeString(v1);
            dest.writeInt(v2);
            dest.writeDouble(v3);
        }
    }
}
